# Shuttle Hierarchical FTP Client
# Version 0.8
# (c) 2008-2009 NIXON Development Corporation.

import ftp, remote, local, transfer, common, prefManager, menus
from ftplib import error_temp, error_perm, error_proto
import browser
from listing import *
from alert import alertBox
import wx, os, stat, sys, time
import wx.lib.delayedresult as delayedResult

class mainFrame(wx.Frame):
	mFTP = None
	infoDlg = None

	def customize(self):
		self.boxWidth = self.prefs.getPref('browser.boxWidth', 200, int=True)
		self.snapSizes = self.prefs.getPref('main.snapWindowSize', True)
		self.pathToIcons = self.prefs.doPrefs('global.pathToIcons', 'icons/osx_native/')
		self.strIconExt = self.prefs.doPrefs('global.iconExt', '.png')
		self.relativeDates = self.prefs.getPref('global.relativeDates', True)
		self.autoFocus = self.prefs.getPref('main.autoFocus', True)
		self.quickScan = self.prefs.getPref('main.quickScan', True)
		self.blBrowser = self.prefs.getPref('main.browser', True)
		#self.blThreading = self.prefs.getPref('global.threading', True)
		self.blStop = False
		self.blAuto = True
		
	def onClose(self, event):
		self.blClosing = True
		self.blStop = True
		self.prefs.savePrefs()
		try: os.remove('deleteme')
		except: pass
		try:
			if self.mFTP.tr.transferBusy:
				event.Veto()
		except: pass
		finally:
			self.Destroy()

	def __init__(self):
		self.prefs = prefManager.prefManager()
		self.prefs.start('prefs.prf')
		self.customize()
		self.jobId = -1
		browser.preview = imagePreview(self)
		self.preview = browser.preview
		self.blClosing = False
		
		wx.Frame.__init__(self, parent=None, id=-1, title='Shuttle', style=wx.DEFAULT_FRAME_STYLE | wx.FRAME_EX_METAL)
		self.SetClientSize((self.boxWidth * 4, 555))
		self.SetMenuBar(menus.menuBar(self))
		self.Bind(wx.EVT_SIZE, self.onFrameSize)
		self.Bind(wx.EVT_CLOSE, self.onClose)
		
		self.blWin = wx.Platform == '__WXMSW__'
		self.blMac = wx.Platform == '__WXMAC__'
		
		if self.blWin:
			monoFont = 'Lucida Console'
			wx.defaultFont = 'Tahoma'
			wx.smallFontSize = 8
			wx.medFontSize = 9
			wx.multiply = '\xD7'
			logHeight = -70
		else:
			if self.blMac:
				monoFont = 'Monaco'
				wx.defaultFont = 'Lucida Grande'
			else:
				monoFont = ''
				wx.defaultFont = ''
			
			wx.smallFontSize = 10
			wx.medFontSize = 12
			wx.multiply = 'x'
			logHeight = -100
			
		wx.boldFont = wx.Font(wx.medFontSize, wx.DEFAULT, wx.NORMAL, wx.FONTWEIGHT_BOLD, False)
		
		iconBundle = wx.IconBundle()
		for x in ('icon16','icon32','icon48','icon64','icon128'):
			try:
				iconBundle.AddIcon(wx.Icon('icons/' + x + '.png', wx.BITMAP_TYPE_PNG))
			except: pass
		
		self.SetIcons(iconBundle)
		self.iconPackage = iconPackage(self.pathToIcons, self.strIconExt)

		spStyle = wx.SP_LIVE_UPDATE# | wx.BORDER_NONE
		
		sizerMain = wx.BoxSizer(wx.VERTICAL)

		# Main
		self.pnAccount = wx.Panel(self, style=wx.TAB_TRAVERSAL)

		fieldStyle = wx.TE_PROCESS_ENTER
		sizerStyle = wx.LEFT | wx.TOP | wx.BOTTOM | wx.ALIGN_CENTER_VERTICAL
		checkStyle = wx.LEFT | wx.ALIGN_CENTER_VERTICAL
		
		sizerAcct = wx.BoxSizer(wx.HORIZONTAL)
		
		self.txtHost = wx.TextCtrl(self.pnAccount, style=fieldStyle, value='ftp.telus.net')
		self.txtHost.SetToolTip(wx.ToolTip('Hostname'))
		sizerAcct.Add(self.txtHost, 2, sizerStyle, 4)

		self.txtUser = wx.TextCtrl(self.pnAccount, style=fieldStyle)
		self.txtUser.SetToolTip(wx.ToolTip('Username'))
		sizerAcct.Add(self.txtUser, 2, sizerStyle, 4)
		
		self.txtPassword = wx.TextCtrl(self.pnAccount, style=fieldStyle | wx.TE_PASSWORD)
		self.txtPassword.SetToolTip(wx.ToolTip('Password'))
		sizerAcct.Add(self.txtPassword, 2, sizerStyle, 4)
		
		self.chkPassive = wx.CheckBox(self.pnAccount, label='&Passive')
		self.chkPassive.SetToolTip(wx.ToolTip('Enable if server uses firewall or NAT.'))
		self.chkPassive.SetValue(True)
		sizerAcct.Add(self.chkPassive, 0, checkStyle, 4)
		
		self.btnConnect = wx.Button(self.pnAccount, label='&Connect', style=wx.BU_EXACTFIT)
		self.btnConnect.SetToolTip(wx.ToolTip('Connect'))
		sizerAcct.Add(self.btnConnect, 0, checkStyle, 4)
		
		sizerAcct.Add((0,0), 1, wx.EXPAND)
		
		self.txtFilter = filterBox(self.pnAccount, 'Filter', fieldStyle)
		sizerAcct.Add(self.txtFilter, 2, wx.ALL, 4)
		
		self.btnNoFilter = wx.BitmapButton(self.pnAccount, bitmap=self.iconPackage.iconCancel, style=wx.BORDER_NONE)
		self.btnNoFilter.SetBitmapSelected(self.iconPackage.iconCancelDown)
		self.btnNoFilter.Bind(wx.EVT_BUTTON, self.onBtnNoFilter)
		self.btnNoFilter.Show(False)
		sizerAcct.Add(self.btnNoFilter, 0, wx.RIGHT | wx.ALIGN_CENTER_VERTICAL, 2)

		self.pnAccount.SetSizer(sizerAcct)
		
		sizerMain.Add(self.pnAccount, 0, wx.EXPAND) # Sizer
		
		self.splitMain = wx.SplitterWindow(self, size=(632, 584), style=spStyle)
		sizerMain.Add(self.splitMain, 1, flag=wx.EXPAND) # Sizer
		
		self.splitMain.SetSashGravity(0.9)

		# Browsers
		self.splitBrowser = wx.SplitterWindow(self.splitMain, size=(632, 408), style=spStyle)

		if self.blBrowser:
			self.pnLocal = browser.browserPanel(self, self.boxWidth, True, True)
			self.pnRemote = browser.browserPanel(self, self.boxWidth, False)
			self.splitBrowser.SplitHorizontally(self.pnRemote, self.pnLocal)
		else:
			self.pnLocal = listingPanel(self, True)
			self.pnRemote = listingPanel(self, False)
			self.splitBrowser.SplitVertically(self.pnRemote, self.pnLocal)
		
		self.splitBrowser.SetSashGravity(0.5)

		# Bottom
		self.splitBottom = wx.SplitterWindow(self.splitMain, style=spStyle)
		
		self.pnLog = wx.Panel(self.splitBottom)

		logSizer = wx.BoxSizer(wx.VERTICAL)
		
		self.txtLog = common.logCtrl(self.pnLog,  style=wx.VSCROLL | wx.TE_WORDWRAP | wx.TE_MULTILINE | wx.BORDER_NONE)
		self.txtLog.SetFont(wx.Font(8, wx.MODERN, wx.NORMAL, wx.NORMAL, False, monoFont))
		self.txtLog.SetEditable(False)
		logSizer.Add(self.txtLog, 1, flag=wx.EXPAND)
		
		self.pnLog.SetSizer(logSizer)
		self.pnLog.Layout()
		
		self.pnQueue = wx.Panel(self.splitBottom)
		#self.pnQueue.SetBackgroundColour(wx.WHITE)
		self.pnQueue.SetFont(wx.Font(wx.smallFontSize, wx.DEFAULT, wx.NORMAL, wx.NORMAL, False))
		self.pnQueue.Bind(wx.EVT_SIZE, self.onQueueSize)
		
		queueTopSizer = wx.BoxSizer(wx.VERTICAL)
		
		nameSizer = wx.BoxSizer(wx.HORIZONTAL)
		
		self.sbActivity = wx.StaticBitmap(self.pnQueue)
		nameSizer.Add(self.sbActivity, 0, wx.RIGHT | wx.LEFT, 3)
		
		self.stTransName = wx.StaticText(self.pnQueue, label='undefined')
		self.stTransName.SetFont(wx.Font(wx.smallFontSize, wx.SWISS, wx.NORMAL, wx.FONTWEIGHT_BOLD, False, wx.defaultFont))
		nameSizer.Add(self.stTransName, 1, wx.EXPAND | wx.ALIGN_CENTER_VERTICAL)
		
		self.stTransProgress = wx.StaticText(self.pnQueue)
		self.stTransProgress.SetForegroundColour((102,102,102))
		nameSizer.Add(self.stTransProgress, 0, wx.ALL | wx.ALIGN_RIGHT, 2)
		
		queueTopSizer.Add(nameSizer, 0, wx.EXPAND | wx.ALIGN_CENTER)
		
		queueGauge = wx.BoxSizer(wx.HORIZONTAL)
		self.gaugeProgress = wx.Gauge(self.pnQueue, range=100, size=(-1, 16), style=wx.GA_SMOOTH)
		queueGauge.Add(self.gaugeProgress, 1, wx.EXPAND | wx.LEFT | wx.RIGHT, 4)
		
		self.btnStop = wx.BitmapButton(self.pnQueue, bitmap=self.iconPackage.iconCancel, style=wx.BORDER_NONE)
		self.btnStop.SetBitmapSelected(self.iconPackage.iconCancelDown)
		self.btnStop.Bind(wx.EVT_BUTTON, self.onBtnStop)
		queueGauge.Add(self.btnStop, 0, wx.RIGHT | wx.ALIGN_CENTER_VERTICAL, 4)
		
		queueTopSizer.Add(queueGauge, 0, wx.EXPAND | wx.ALIGN_CENTER)
		
		self.lblTransFrom = common.titleLabel('From', self.pnQueue)
		self.lblTransTo = common.titleLabel('To', self.pnQueue)
		#self.lblTransRemain = titleLabel('ETA', self.pnQueue)
		
		self.stTransFrom = wx.StaticText(self.pnQueue)
		self.stTransTo = wx.StaticText(self.pnQueue)
		self.stTransRemain = wx.StaticText(self.pnQueue)
		self.stTransRemain.SetForegroundColour((102,102,102))
		
		sizerInfoGrid = wx.FlexGridSizer(cols=2, hgap=2, vgap=2)
		
		styleLeft = wx.ALIGN_RIGHT | wx.ALIGN_CENTER_VERTICAL
		styleRight = wx.EXPAND
		
		queueTopSizer.Add(self.stTransRemain, 0, wx.BOTTOM | wx.LEFT, 3)
		
		sizerInfoGrid.AddGrowableCol(1)
		
		sizerInfoGrid.Add(self.lblTransFrom, 0, styleLeft)
		sizerInfoGrid.Add(self.stTransFrom, 0, styleRight)
		
		sizerInfoGrid.Add(self.lblTransTo, 0, styleLeft)
		sizerInfoGrid.Add(self.stTransTo, 0, styleRight)
		
		queueTopSizer.Add(sizerInfoGrid, 1, wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.ALIGN_CENTER | wx.EXPAND, 4)
		
		queueSizer = wx.BoxSizer(wx.VERTICAL)
		queueSizer.AddStretchSpacer()
		queueSizer.Add(queueTopSizer, 0, wx.ALIGN_CENTER | wx.EXPAND)
		queueSizer.AddStretchSpacer()

		self.pnQueue.SetSizer(queueSizer)
		self.pnQueue.Fit()
		self.pnQueue.Show(False)
		
		self.splitBottom.Initialize(self.pnLog)
		self.splitBottom.SetSashGravity(0.5)
		self.splitBottom.Bind(wx.EVT_SPLITTER_SASH_POS_CHANGED, self.onSplitBottom)
		self.splitBottom.intRecallPos = -1
		
		self.splitMain.SplitHorizontally(self.splitBrowser, self.splitBottom, logHeight)

		self.SetSizer(sizerMain)

		# Events
		for x in (self.txtUser, self.txtHost, self.txtPassword):
			x.Bind(wx.EVT_TEXT_ENTER, self.onTxtEnter)
			if x != self.txtPassword:
				x.Bind(wx.EVT_TEXT, self.clearPassword)
		
		self.btnConnect.Bind(wx.EVT_BUTTON, self.onBtnConnect)
		
		# Minimum Pane Sizes
		
		if self.blMac:
			self.splitMain.SetSashSize(8)
			self.splitBrowser.SetSashSize(8)
			wx.SystemOptions.SetOptionInt('mac.listctrl.always_use_generic', False)
			
			font = wx.Font(12, wx.SWISS, wx.NORMAL, wx.NORMAL, False)
			for ctrl in (self.pnAccount, self.txtHost, self.txtUser, self.txtPassword, \
				self.chkPassive, self.btnConnect, self.txtFilter):
				ctrl.SetFont(font)
		else:
			self.txtLog.SetBackgroundColour((0,0,0))
			self.txtLog.SetForegroundColour((255,255,255))
			sys.stdout = self.txtLog
		
		self.splitMain.SetMinimumPaneSize(24)
		self.splitBrowser.SetMinimumPaneSize(64)
		
		self.setup()

	def setup(self):
		self.loadResources()
		
		if self.blWin:
			self.homepath = os.getenv('USERPROFILE').replace('\\','/')

			docPath5 = '/My Documents'
			
			try:
				os.listdir(self.homepath + docPath5) #>= Windows 2000/XP
				self.homepath += docPath5
			except: pass
			
			hd = self.homepath.split('/')[0]
			self.pnLocal.addListBox(hd, True, True)
			self.driveList = local.getDrives(self.quickScan)
			
			if self.blBrowser:
				lstBox = self.pnLocal.lstBrowsers[0]
			else:
				lstBox = self.pnLocal.lstListing
			for x in self.driveList:
				lstBox.InsertImageStringItem(lstBox.GetItemCount() + 1, x.upper(), 3)
		else:
			self.homepath = os.getenv('HOME')
			hd = '/'
		
		if self.blBrowser:
			self.pnLocal.setPreview(False)
			self.pnRemote.setPreview(False)
			
		self.pnLocal.addListBox(hd + '/', True, True)
		self.TraverseLocal(self.homepath)
		self.pnRemote.addListBox('/', False)
	
	def onFrameSize(self, event, size=None):
		if self.snapSizes:
			if size:
				clientWidth = size
			else:
				clientWidth = self.GetClientSize()[0]
			boxWidths = clientWidth / self.boxWidth
			diff = self.boxWidth * boxWidths
			self.SetClientSize((diff, self.GetClientSize()[1]))

		if event: event.Skip()
		
	def onBtnConnect(self, event):
		self.resetFTP()
		
	def onBtnStop(self, event):
		self.blStop = True
		
	def onTxtEnter(self, event):
		self.resetFTP()
		
	def onBtnNoFilter(self, event):
		self.txtFilter.unFilter()
		self.btnNoFilter.Show(False)
		self.pnAccount.Layout()
			
	def clearPassword(self, event):
		self.txtPassword.Value = ''
		
	def getTransferType(self):
		#return self.radAscii.GetValue()
		return self.GetMenuBar().mnuType.IsChecked(254)
		
	def onSplitBottom(self, event):
		self.splitBottom.intRecallPos = event.GetSashPosition()
		
	def onQueueSize(self, event):
		y = event.GetSize()[1]
		blShow = True
		blFrom = True
		blTo = True
		
		intTest = 4
		for x in (self.gaugeProgress, self.stTransProgress, self.stTransName):
			intTest += x.GetSize()[1]
		intFrom = intTest + self.lblTransFrom.GetSize()[1] + 2
		intTo = intFrom + self.lblTransTo.GetSize()[1] + 2
		
		if y < intTest:
			blShow = False
		else:
			if y < intFrom:
				blFrom = False
			if y < intTo:
				blTo = False
			
		for x in (self.lblTransFrom, self.stTransFrom):
			x.Show(blShow and blFrom)
			
		for x in (self.lblTransTo, self.stTransTo):
			x.Show(blShow and blTo)
			
		self.pnQueue.Layout()
			
	def doQueue(self, blTransfer):
		self.pnQueue.Show(blTransfer)
		if not(blTransfer):
			self.splitBottom.Unsplit(self.pnQueue)
			self.gaugeProgress.SetValue(0)
			self.stTransName.SetLabel('undefined')
			self.stTransProgress.SetLabel('')
		else:
			if self.splitBottom.intRecallPos != -1:
				self.splitBottom.SplitVertically(self.pnLog, self.pnQueue, self.splitBottom.intRecallPos)
			else:
				self.splitBottom.SplitVertically(self.pnLog, self.pnQueue, self.GetSize()[0] * 0.6)
			self.pnQueue.Refresh()
			
	def loadResources(self):
		deflist = '.txt .html .css .js .xml'
		try:
			if os.access('asclist.txt', os.F_OK):
				print ('Using external ASCII file list')
				fl = open('asclist.txt', 'r')
				self.asclist = fl.read()
				fl.close()
				return
				
		except: pass
		
		print ('ASCII file list could not be found; using default')
		self.asclist = deflist
			
	def TraverseLocal(self, path):
		if self.blBrowser:
			lst = self.pnLocal.lstBrowsers
			str = path.split('/')
			if self.blWin:
				if str[0].find(':') != -1:
					idx = lst[0].getStrings().index(str[0])
					lst[0].setSelection(idx)
			
			ls = len(str)
			lenBoxes = len(lst) - 1
			lenCDUP = 0

			for x in range(0, ls):
				if str[x] == '..':
					self.pnLocal.removeListBox(len(lst)-1)
					lenBoxes -= 1
					lenCDUP += 1
				else:
					self.pnLocal.addListBox(str[x], True, True)
			
			for x in range(0, lenCDUP):
				str.pop(x)
				
			delayedResult.startWorker(self.traverseLocalConsumer, self.traverseLocalProducer, wargs=(str, lenBoxes))
				
			print('Traversed %d folders.' % (ls + 1))
			
	def traverseLocalProducer(self, tpFolders, lenBoxes):
		lbl = ''
		for x in range(0, len(tpFolders)):
			if x > 0:
				lbl = self.pnLocal.lstBrowsers[lenBoxes + x].Label + '/'
				
			sel = lbl + tpFolders[x] + '/'
			self.doLocal(sel, lenBoxes + x + 1, True)
		
	def traverseLocalConsumer(self, delayedresult):
		try:
			result = delayedresult.get()
		except Exception, e:
			print('Error in shuttle.traverseLocal: %s' % e)
			return
		
		delayedResult.startWorker(self.pnLocal.scrollEffectConsumer, self.pnLocal.scrollEffect)
		
		if result == -1:
			self.pnLocal.removeListBox(len(self.pnLocal.lstBrowsers) - 1)

	def traverseProducer(self, tpFolders, lenBoxes):
		boxes = self.pnRemote.lstBrowsers
		ls = len(tpFolders)
	
		for x in range(0, ls):
			if x == ls - 1:
				pwf = boxes[lenBoxes + x - 1].Label + '/' + tpFolders[x]
				type = self.dirList.getPerms(pwf)[:1]
				if type == 'd' or type == 'l': pass
				else:
					return True
			
			currBox = boxes[lenBoxes + x]
			lastBox = boxes[lenBoxes + x - 1]
			
			strLabel = lastBox.Label + '/' + currBox.Label
			
			currBox.ignoreListEvents = True
			self.doList(strLabel, lenBoxes + x, True)
			currBox.ignoreListEvents = False
			
			idx = lastBox.getStrings().index(tpFolders[x])
			
			lastBox.ignoreListEvents = True
			lastBox.setSelection(idx)
			lastBox.Focus(idx)
			lastBox.ignoreListEvents = False
		
		return False
		
	def traverseConsumer(self, delayedresult):
		try:
			result = delayedresult.get()
		except Exception, e:
			print('Error in shuttle.traverseProducer: %s' % e)
			return
			
		self.pnRemote.doThrobber(False)
		self.pnRemote.doStatus(False)
		
		if result == -1:
			self.pnRemote.removeListBox(len(self.pnRemote.lstBrowsers) - 1)

		
	def TraversePath(self, path, cwd):
		box = self.pnRemote.lstBrowsers
		#self.pnRemote.spBrowser.Scroll(0, 0)
		#self.mFTP.chgDir(cwd)
		
		if path[0:1] == '/':
			path = path[1:]
		if path[-1:] == '/':
			path = path [:-1]
			
		str = path.split('/')
			
		ls = len(str)
		lenBoxes = len(box)
		lenCDUP = 0
		
		for x in range(0, ls):
			if str[x] == '..':
				self.pnRemote.removeListBox(len(self.pnRemote.lstBrowsers)-1)
				lenBoxes -= 1
				lenCDUP += 1
			else:
				self.pnRemote.addListBox(str[x])
			
		for x in range(0, lenCDUP):
			str.pop(x)

		self.jobId += 1
		self.pnRemote.doStatus(True)
		self.pnRemote.doThrobber(True)
		self.mFTP.tl.addTask(self.traverseProducer, (str, lenBoxes), self.traverseConsumer)
			
		print('Traversed %d folders.' % ls)
		
	def UpdateLocal(self, path, filter=-1):
		self.localFilter = filter
		self.localPath = path
		self.pnLocal.doStatus()
		self.pnLocal.doThrobber()
		delayedResult.startWorker(self.updateLocalConsumer, local.getNames, wargs=(path,))
	
	def updateLocalConsumer(self, delayedresult):
		try:
			result = delayedresult.get()
			
		except Exception, e:
			print('Error in local.getNames: %s' % e)
			return
		
		self.doLocal(result)
		
	def doLocal(self, path, filter=-1, block=False):		
		if block:
			result = local.getNames(path)
			self.localFilter = filter
		else:
			result = path
			path = self.localPath
			
		if self.blBrowser:
			box = self.pnLocal.lstBrowsers[self.localFilter]
		else:
			box = self.pnLocal.lstListing
			
		if self.blWin and box.Number == 1: return
		dirpos = 0
		box.DeleteAllItems()
		
		filterText = self.txtFilter.GetValue()
		
		if path[-1:] == '/':
			displayPath = path[:-1]
		else:
			displayPath = path
		box.Label = displayPath
		
		for f in result:
			if self.txtFilter.blFilter: # filtering
				if f.lower().find(filterText.lower()) == -1: continue
				
			type = local.getType(path + '/' + f)
			
			if type == 2: # directory
				box.InsertImageStringItem(dirpos, f, type)
				dirpos += 1
			else:
				box.InsertImageStringItem(box.GetItemCount() + 1, f, type)
				
		if not(self.blBrowser): box.setBackColours()
		
		self.pnLocal.doStatus(False)
		self.pnLocal.doThrobber(False)
		
	def UpdateList(self, path, reload=False):
		if self.dirList.indexed(path) and reload==False:
			self.doList(path)
		else:
			self.pnRemote.doStatus(True)
			self.pnRemote.doThrobber(True)
			self.path = path
			self.mFTP.tl.addTask(self.dirList.doPath, (path, self.mFTP, not(reload)), self.updateListConsumer, True)
	
	def updateListConsumer(self, delayedresult):
		path = -1
		try:
			path = delayedresult.get()
		except error_perm, e: pass
		except Exception: pass
			
		if path == -1:
			dlg = alertBox(self, \
				'You do not have permission to list the contents of "' + self.path + '"', \
				'Make sure you have the appropriate access permissions for this folder.')
		else:
			self.doList(path)
			
		self.pnRemote.doStatus(False)
		self.pnRemote.doThrobber(False)
			 
	def doList(self, path, filter=-1, block=False):
		if block:
			self.dirList.doPath(path, self.mFTP, True)
		if self.blBrowser:
			lb = self.pnRemote.lstBrowsers[filter]
		else:
			lb = self.pnRemote.lstListing
				
		lb.DeleteAllItems()
		dirpos = 0
		filterText = self.txtFilter.GetValue()
			
		for f in self.dirList.getNames(path):
			if self.txtFilter.blFilter: # filtering
				if f.lower().find(filterText.lower()) == -1: continue
				
			pwf = path + '/' + f
			type = self.dirList.getPerms(pwf)[:1]
			
			if type == 'd':
				pos = lb.InsertImageStringItem(dirpos, f, 2)
				dirpos += 1
			else:
				if type == 'l':
					intImg = 1
				else:
					intImg = 0
				pos = lb.InsertImageStringItem(lb.GetItemCount() + 1, f, intImg)
				
			if not(self.blBrowser):
				size = format.parseSize(self.dirList.getSizes(pwf))
				date = format.parseDate(self.dirList.getDates(pwf), self.relativeDates)
				lb.SetStringItem(pos, 1, size)
				lb.SetStringItem(pos, 2, date)
				
		if not(self.blBrowser): box.setBackColours()
		
		lb.Label = path
		self.mFTP.tl.hpBlock = False
		
	def resetFTP(self):
		self.txtLog.Value += '\r\n'
		if self.blBrowser:
			self.pnRemote.removeListBox(1)
			self.pnRemote.spBrowser.Scroll(0, 0)	
		self.pnRemote.doStatus()
		self.pnRemote.doThrobber()
		self.mFTP = ftp.ftpMaker((self.txtHost.Value, self.txtUser.Value, self.txtPassword.Value, ''), self, self.chkPassive.GetValue())
		
	def reloadRemote(self, num):
		box = self.pnRemote.lstBrowsers			
		lbl = box[num].Label
		if lbl == '': lbl = '/'
		self.UpdateList(lbl, True)
		
	def onReloadAll(self, event):
		# Just for the aesthetic effect :)
		s = self.pnRemote.lstBrowsers[-1].Label
		self.pnRemote.removeListBox(1)
		if s != '/':
			self.TraversePath(s, '/')
			
class imagePreview():
	def __init__(self, prnt):
		self.prnt = prnt
		self.tempFile = 'deleteme'
		
	def scaleImage(self, bmp, intMax):
		x = float(bmp.GetWidth())
		y = float(bmp.GetHeight())
		img = None
		
		blXOver = x > intMax
		
		if blXOver:
			img = bmp.ConvertToImage()
			ratio = float(y / x)
			img = img.Scale(intMax, ratio * intMax, wx.IMAGE_QUALITY_HIGH)
			blYOver = ratio * intMax > intMax
		else:
			blYOver = y > intMax
		
		if blYOver:
			img = bmp.ConvertToImage()
			ratio = float(x / y)
			img = img.Scale(ratio * intMax, intMax, wx.IMAGE_QUALITY_HIGH)
			
		if img:
			return wx.BitmapFromImage(img)
		else:
			return bmp
			
	def needsPreview(self, name):
		blPreview = False
		ext = name[name.rfind('.') + 1:].lower()
		for x in ('gif', 'jpg', 'jpeg', 'png', 'bmp', 'tiff', 'tif', 'tga', 'xpm', 'cur', 'ico', 'ani', 'pnm', 'pcx'):
			if ext == x:
				blPreview = True
				break
				
		return blPreview
		
	def windowPreview(self, pwf, blLocal):
		isBmp = self.needsPreview(pwf)
		frame = common.previewWindow(isBmp, pwf)
		if blLocal:
			if isBmp:
				frame.setImage(wx.Bitmap(pwf))
			else:
				frame.loadText(pwf)
		else:
			self.frame = frame
			self.isBmp = isBmp
			self.prnt.mFTP.tl.addTask(self.transfer.transferFile, (-2, pwf, self.tempFile, None), self.windowConsumer, True)
			
	def windowConsumer(self, delayedresult):
		try:
			if self.isBmp:
				self.frame.setImage(wx.Bitmap(self.tempFile))
			else:
				self.frame.loadText(self.tempFile)
			
		except Exception, e:
			print('Error in imagePreview.windowConsumer: %s' % e)
			
	def imagePreview(self, pwf, blLocal, callback=None):
		if blLocal:
			return wx.Bitmap(pwf)
		else:
			self.callback = callback
			self.prnt.mFTP.tl.addTask(self.transfer.transferFile, (-2, pwf, self.tempFile, None), self.imageConsumer, True)
			
	def imageConsumer(self, result):
		try:
			self.callback(wx.Bitmap(self.tempFile))
			
		except Exception, e:
			print('Error in imagePreview.imageConsumer: %s' % e)

	def filePreview(self, pwf, callback, frame = None, blLocal = False):
		if blLocal:
			self.FilePreview(pwf, callback, frame, blLocal)
		else:
			self.prnt.mFTP.tl.addTask(self.FilePreview, (pwf, callback, frame, blLocal), self.previewConsumer, True)
	
	def FilePreview(self, pwf, callback, frame, blLocal):
		sel = format.getNameFromPath(pwf)
		
		if self.needsPreview(pwf) and blLocal:
			if not frame:
				frame = common.previewWindow(True, sel)
			frame.setImage(wx.Bitmap(pwf))
		else:
			if blLocal:
				strFile = pwf
			else:
				strFile = 'deleteme'
				
				self.transfer.transferFile(-2, pwf, strFile)
				
		self.frame = frame
			
	def previewConsumer(self, delayedresult):
		try:
			result = delayedresult.get()
			if frame:
				strReason = None
				try:
					if frame.bmp:
						frame.setImage(wx.Bitmap(strFile))
					else:
						fl = open(strFile)
						text = fl.read()
						fl.close()
						frame.textCtrl.SetValue(text)
						frame.Show(True)
				except UnicodeDecodeError:
					strReason = ' because it is a binary file other than an image.'
				except Exception, e:
					strReason = ': ' + str(e)
				finally:
					if strReason:
						print('Could not preview %s' % pwf + strReason)
						frame.Destroy()
			else:
				callback(strFile)
		except Exception, e:
			print('Error in imagePreview.previewConsumer: %s' % e)
				
class filterBox(common.initialTextCtrl):
	def __init__(self, prnt, initialText, style):
		common.initialTextCtrl.__init__(self, prnt, initialText, style)
		self.Bind(wx.EVT_TEXT, self.onFilter)
		self.prnt = prnt.GetParent()
		self.f = self.filterTimer(self)
		self.blFilter = False
		
	class filterTimer(wx.Timer):
		def __init__(self, prnt):
			self.prnt = prnt
			wx.Timer.__init__(self)
		
		def Notify(self):
			self.prnt.updateAll(True)
		
	def onFilter(self, event):
		if self.blInitial == False:
			val = self.GetValue()
			self.prnt.btnNoFilter.Show(val != '')
			self.blFilter = val != ''
			self.prnt.pnAccount.Layout()
			self.f.Start(300, True)
	
	def unFilter(self):
		self.setInitialText()
		self.blFilter = False
		self.updateAll(False)
		
	def updateAll(self, filter):
		boxes = self.prnt.pnLocal.lstBrowsers
		
		if filter:
			txt = self.GetValue()
		else:
			txt = ''
		
		if self.prnt.blWin:
			intStart = 1
		else:
			intStart = 0
			
		for x in range(intStart, len(boxes)):
			self.prnt.doLocal(boxes[x].Label + '/', x, True)
			
		if self.prnt.mFTP != None:
			boxes = self.prnt.pnRemote.lstBrowsers
			for x in range(0, len(boxes)):
				self.prnt.doList(boxes[x].Label, x)
				
		self.prnt.pnRemote.doStatus(False)
		self.prnt.pnRemote.doThrobber(False)
			
	def onDeactivate(self, event):
		common.initialTextCtrl.onDeactivate(self, event)
		
class iconPackage:
	def __init__(self, pathToIcons, iconExt):
		self.iconPreFolder = wx.Bitmap(pathToIcons + 'pre_folder' + iconExt)
		self.iconPreFile = wx.Bitmap(pathToIcons + 'pre_file' + iconExt)
		self.iconPreLink = wx.Bitmap(pathToIcons + 'pre_link' + iconExt)
		self.iconPreDrive = wx.Bitmap(pathToIcons + 'pre_drive' + iconExt)
		self.iconPreMulti = wx.Bitmap(pathToIcons + 'pre_multi' + iconExt)
		self.iconPreComputer = wx.Bitmap(pathToIcons + 'pre_computer' + iconExt)
		self.iconPreServer = wx.Bitmap(pathToIcons + 'pre_server' + iconExt)
		
		self.iconSmallComputer = wx.Bitmap(pathToIcons + 'sm_computer' + iconExt)
		self.iconSmallServer = wx.Bitmap(pathToIcons + 'sm_server' + iconExt)
		self.iconSmallDir = wx.Bitmap(pathToIcons + 'sm_folder' + iconExt)
		self.iconSmallFile = wx.Bitmap(pathToIcons + 'list_genericfile' + iconExt)
		self.iconSmallDrive = wx.Bitmap(pathToIcons + 'sm_drive' + iconExt)
		self.iconSmallLink = wx.Bitmap(pathToIcons + 'list_link' + iconExt)
		self.iconThrobber = wx.Bitmap(pathToIcons + 'throbber.gif')
		self.iconSmallError = wx.Bitmap(pathToIcons + 'error_sm' + iconExt)
		
		self.iconNextDir = wx.Bitmap('icons/global/nextdir.gif')
		
		self.iconCancel = wx.Bitmap('icons/global/cancel.gif')
		self.iconCancelDown = wx.Bitmap('icons/global/cancel-down.gif')
		
		self.imageList = wx.ImageList(16, 16, True)
		try:
			self.imageList.Add(self.iconSmallFile)
			self.imageList.Add(self.iconSmallLink)
			self.imageList.Add(self.iconSmallDir)
			self.imageList.Add(self.iconSmallDrive)
		except Exception, e:
			print('iconPackage: Could not load icons')
			print e

app = wx.PySimpleApp()
mainFrame().Show(True)
app.MainLoop()